/**
 * This file is part of 1genia trampoline
 * Copyright (C) 2007 1genia (contact@1genia.com)
 *
 * This library is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; version 3 of the License. 
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details. 
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; see the file COPYING.TXT.  If not,
 * write to the Free Software Foundation, Inc., 51 Franklin Street,
 * Fifth Floor, Boston, MA 02110-1301, USA. 
 **/
package com.genia.toolbox.basics.editor.gui.view.internal;

import java.rmi.server.UID;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.JDesktopPane;

import com.genia.toolbox.basics.editor.gui.AbstractGUI;
import com.genia.toolbox.basics.editor.model.document.Document;
import com.genia.toolbox.basics.editor.model.document.Element;

/**
 * The basic document desktop.
 * 
 * @param <G>
 *          The generic gui.
 * @param <I>
 *          The generic initial settings.
 * @param <D>
 *          The generic document.
 * @param <E>
 *          The generic element.
 */
@SuppressWarnings("serial")
public abstract class AbstractDocumentDesktop<G extends AbstractGUI<?, ?, ?, D, E, ?>, I extends AbstractDocumentInternalFrame<G, ?, D, E, ?>, D extends Document<E>, E extends Element<?>>
    extends JDesktopPane
{

  /**
   * The application gui.
   */
  public G gui;

  /**
   * The initial x position of a frame.
   */
  public final int xOffset = 10;

  /**
   * The initial y position of a frame.
   */
  public final int yOffset = 10;

  /**
   * The documents.
   */
  public Map<UID, I> documents = null;

  /**
   * The unique ID of the selected document.
   */
  public UID selectedDocumentUID = null;



  /**
   * Constructor.
   * 
   * @param gui
   *          the gui application.
   */
  public AbstractDocumentDesktop(G gui)
  {
    super();
    this.gui = gui;
    this.setDragMode(JDesktopPane.LIVE_DRAG_MODE);
    this.documents = new HashMap<UID, I>();
  }



  /**
   * Get the documents.
   * 
   * @return the documents.
   */
  public Map<UID, I> getDocuments()
  {
    return this.documents;
  }



  /**
   * Get the application gui.
   * 
   * @return the application gui.
   */
  public G getGui()
  {
    return this.gui;
  }



  /**
   * Close the internal frame of document the unique ID.
   * 
   * @param documentUniqueID
   *          the document the unique ID.
   */
  public void close(UID documentUniqueID)
  {
    I internalFrame = this.documents.get(documentUniqueID);
    if (internalFrame != null) {
      internalFrame.setVisible(false);
      this.documents.remove(documentUniqueID);
      this.remove(internalFrame);
    }
    this.setSelectedFrame(null);
    this.getGui().displayElementSettings(null);
  }



  /**
   * Get the list of all the displayed documents unique ID.
   * 
   * @return the list of all the displayed documents unique ID.
   */
  @SuppressWarnings("unchecked")
  public List<UID> getAllDocumentsUniqueID()
  {
    return new ArrayList(this.documents.keySet());
  }



  /**
   * Get the document model from its unique ID.
   * 
   * @param documentUniqueID
   *          the document unique ID.
   * @return the document model.
   */
  public Document<E> getDocument(UID documentUniqueID)
  {
    I internalFrame = this.documents.get(documentUniqueID);
    return internalFrame.getDocument();
  }



  /**
   * Get the current selected document model.
   * 
   * @return the current selected document model.
   */
  public D getSelectedDocument()
  {
    D model = null;
    I internalFrame = this.documents.get(this.selectedDocumentUID);
    if (internalFrame != null) {
      model = internalFrame.getDocument();
    }
    return model;
  }



  /**
   * Set the current selected document model.
   * 
   * @param documentUniqueID
   *          the current selected document model.
   */
  public void setSelectedDocument(UID documentUniqueID)
  {
    I internalFrame = this.documents.get(documentUniqueID);
    if (internalFrame != null) {
      this.selectedDocumentUID = documentUniqueID;
      this.setSelectedFrame(internalFrame);
    }
  }



  /**
   * Update the document.
   * 
   * @param document
   *          the document model.
   */
  public void updateDocument(D document)
  {
    I internalFrame = this.documents.get(document.getUniqueId());
    if (internalFrame != null) {
      internalFrame.setDocumentModel(document);
    }
  }



  /**
   * Update the frame title of the current selected document.
   */
  public void updateTitle()
  {
    D document = this.getSelectedDocument();
    if (document != null) {
      document.setIsSaved(false);
      I internalFrame = this.documents.get(document.getUniqueId());
      if (internalFrame != null) {
        internalFrame.updateTitle();
      }
    }
  }



  /**
   * Instanciate a new internal frame.
   * 
   * @param model
   *          the document model.
   * @return the new internal frame.
   */
  public abstract I createNewInternalFrame(D model);



  /**
   * Display a document for the fist time. We need to create the internal frame.
   * 
   * @param model
   *          the document model.
   */
  public void firstDisplay(D model)
  {
    int openedFrameCount = this.documents.size() + 1;
    I internalFrame = this.createNewInternalFrame(model);
    internalFrame.setLocation(this.xOffset * openedFrameCount, this.yOffset * openedFrameCount);
    internalFrame.setTitle(model.getName());
    this.add(internalFrame);
    this.documents.put(model.getUniqueId(), internalFrame);
    internalFrame.setVisible(true);
    this.getDesktopManager().activateFrame(internalFrame);
    this.setSelectedDocument(model.getUniqueId());
  }



  /**
   * Display a document. The internal frame already exists.
   * 
   * @param model
   *          the document.
   */
  public void display(D model)
  {
    if (model == null) {
      this.setSelectedDocument(null);
    }
    else {
      I internalFrame = this.documents.get(model.getUniqueId());
      this.getDesktopManager().activateFrame(internalFrame);
      this.setSelectedDocument(model.getUniqueId());
    }
  }



  /**
   * Deactivate the content of the document.
   */
  public void deactivateAll()
  {
    D documentModel = this.getSelectedDocument();
    if (documentModel != null) {
      I internalFrame = this.documents.get(this.getSelectedDocument().getUniqueId());
      if (internalFrame != null) {
        internalFrame.deactivateAll();
      }
    }
  }



  /**
   * Update the element.
   * 
   * @param element
   *          the element model.
   */
  public void updateElement(E element)
  {
    I internalFrame = this.getDocuments().get(element.getDocumentID());
    if (internalFrame != null) {
      internalFrame.setElementModel(element);
    }
  }



  /**
   * Display a sub-element.
   * 
   * @param subElement
   *          the sub-element.
   */
  public void display(E subElement)
  {
    I internalFrame = this.getDocuments().get(subElement.getDocumentID());
    internalFrame.setElementModel(subElement);
  }



  /**
   * Get the selected element.
   * 
   * @return the selected element.
   */
  public E getSelectedElement()
  {
    E element = null;
    D document = this.getSelectedDocument();
    if (document != null) {
      I internalFrame = this.getDocuments().get(document.getUniqueId());
      if (internalFrame != null) {
        element = internalFrame.getSelectedElement();
      }
    }
    return element;
  }



  /**
   * Add a sub element to a container.
   * 
   * @param element
   *          the container.
   */
  public void addSubElement(E element)
  {
    I internalFrame = this.getDocuments().get(element.getDocumentID());
    if (internalFrame != null) {
      internalFrame.addSubElement(element);
    }
  }



  /**
   * Delete a sub element.
   * 
   * @param element
   *          the element.
   */
  public void deleteSubElement(E element)
  {
    I internalFrame = this.getDocuments().get(element.getDocumentID());
    if (internalFrame != null) {
      internalFrame.deleteSubElement(element);
    }
  }



  /**
   * Move up or right a sub element in a container.
   * 
   * @param element
   *          the element.
   */
  public void moveUpOrRightSubElement(E element)
  {
    I internalFrame = this.getDocuments().get(element.getDocumentID());
    if (internalFrame != null) {
      internalFrame.moveUpOrRightSubElement(element);
    }
  }



  /**
   * Move down or left a sub element in a container.
   * 
   * @param element
   *          the element.
   */
  public void moveDownOrLeftSubElement(E element)
  {
    I internalFrame = this.getDocuments().get(element.getDocumentID());
    if (internalFrame != null) {
      internalFrame.moveDownOrLeftSubElement(element);
    }
  }

}
